//
// MoonlightController.cs
//
// Authors:
//   Alan McGovern alan.mcgovern@gmail.com
//
// Copyright (C) 2007 Alan McGovern
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
// 
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//


using System;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Input;
using System.Xml;
using System.Collections.Generic;
using System.IO;
using MonoDevelop.XamlDesigner.Serialization;
using MonoDevelop.XamlDesigner.View;
using MonoDevelop.XamlDesigner.Model;
using Moonlight.Gtk;
using MonoDevelop.DesignerSupport.Toolbox;

namespace MonoDevelop.XamlDesigner.Controller
{
    public class MoonlightController
    {
		public event EventHandler ZoomChanged;
		
        private MoonlightHost moonlight;
		private PropertyManager propertyManager;
        private Serializer serializer;
		private UndoEngine undo;
		private ITool current_tool;
		private ISelection selection;
		private double current_scale = 1.0;
        private int zoom = 100;
        
        //TODO have toggle button to switch between mode and when
        //drawing mode ==> appear a listbox which contain list of Panel
        //select panel to draw on
        //and now we can select whatever we want in design area ==> no impact on mode
        //I think it will be more userfriendly like that!
        //designer mode make appear the combotreebox to select current node
        //made property function of that selection
        //Made 2 class 1 by mode and forward controller to that
        //Made a multiselection in drawing mode but a single selection in designer one
        
        //drag and drop seems very complicated so I will make some lot
        //LOT1 : drawing mode only and select the canvas to use
        //LOT2 : add designer mode
        
        //write xaml and switch to designer could be ok for a start for control
        
        
        
        //TODO it mean that we need to check whenever we need to loop on children of 1st level canvas
        //and whenever we need to use the visitor to visite all subelement recurcivly!
        //class to modify : selection tool, draggedcontroltool, abstracthandlegroup, standard selection
        //TODO cut background canvas from panel one! and where it is used
		//TODO Refactor selector and remove multi selection
		//TODO Fix the serialise tool
		//TODO remove canvas and panel quick hack to have canvas to draw

		internal FrameworkElement Root
        {
			get { return moonlight.Content; }
		}
		
        //or save it
        internal Canvas Canvas
		{
			get { 
                Canvas p  = Selection.MainElement as Canvas;
            	if (p == null)
                    p = ((FrameworkElement)Selection.MainElement).Parent as Canvas;
                return p;
            }
		}
        
		internal MoonlightHost MoonlightHost {
			get { return this.moonlight; }
		}
        
        //TODO move tool inside mode?
		public ITool CurrentTool
		{
			get { return current_tool; }
			set {
				if (current_tool != null)
					current_tool.Deactivate();
				current_tool = value;
				if (current_tool != null)
					current_tool.Activate();
			}
		}
		
		public PropertyManager PropertyManager
		{
			get { return propertyManager; }
		}
		
        public UndoEngine UndoEngine
        {
            get { return undo; }
        }

        public ISelection Selection {
        	get { return selection; }
			protected set { selection = value; }
        }

        public double CurrentScale {
        	get { return current_scale; }
        	set { current_scale = value; }
        }
    	private XamlDesignView view;
        
        public MoonlightController(MoonlightHost moonlight, XamlDesignView view)
        {
			this.moonlight = moonlight;
			this.view = view;
			Init ();
			serializer = new Serializer();
			undo = new UndoEngine();
			Selection = new StandardSelection(this);
			propertyManager = new PropertyManager(this);
        }
        
		
        /*public void Clear()
		{
			Selection.Clear();			
			//Panel.Children.Clear();
			undo.Clear();
		}*/
		
		private void Setup()
		{
            /*bool found = false;
            Utils.EnumVisual(Root, delegate (DependencyObject obj) {
                if (obj is Panel)
                    panel = (Panel)obj;
                	found = true;
                	return;
            });
            if (!found) {
                //write a canvas/panel subnode
                //and reload xaml
                view.AddDefaultCanvas ();
                return;
            }*/

			Root.MouseLeftButtonDown += MouseLeftDown;
            Root.MouseMove += MouseMove;
            Root.MouseLeftButtonUp += MouseLeftUp;
            Root.MouseWheel += MouseWheel;
		}
        
        private void MouseLeftDown(object sender, MouseEventArgs e)
        {
            this.Root.CaptureMouse();
            CurrentTool.MouseDown(sender, e);
        }
        
        private void MouseMove(object sender, MouseEventArgs e)
        {
			CurrentTool.MouseMove(e);
        }
        
        private void MouseLeftUp(object sender, MouseEventArgs e)
        {
            CurrentTool.MouseUp(sender, e);
            this.Root.ReleaseMouseCapture();
            
        }
		
		private void MouseWheel(object sender, MouseWheelEventArgs e)
        {
            int x;
            int y;
            moonlight.GetPointer (out x, out y);
			int w, h;
            moonlight.GetSizeRequest(out w, out h);
            
            if (e.Delta > 0)
                zoom += 10;
            else if (e.Delta < 0)
                zoom -= 10;
            
            Zoom (zoom, new Point(((float)x)/w, ((float)y)/h));
        }
        
		public string SerializeCanvas()
        {
            return serializer.Serialize(Root, xClass);
		}
		
		public void Undo()
        {
            undo.Undo();
			Selection.Update();
        }
		
		public void Redo()
		{
			undo.Redo();
			Selection.Update();
		}
		
		public void Zoom(int zoom, Point center)
		{
            //TODO think if best is transform all (root) or only canvas selected
            //maybe only active on drawing mode...
			double scale = 	zoom / 100.0;
			TransformGroup transforms = new TransformGroup();
			ScaleTransform scaleTransform = new ScaleTransform();
			scaleTransform.ScaleX = scale;
			scaleTransform.ScaleY = scale;
            /*TranslateTransform translate = new TranslateTransform();
            translate.X = 0;
            translate.Y = 0;
            */
			current_scale = scale;
			transforms.Children.Add(scaleTransform);
			Root.SetValue(UIElement.RenderTransformOriginProperty, center);
			Root.SetValue(UIElement.RenderTransformProperty, transforms);
            
			//Console.WriteLine("Canvas1: ({0},{1})", Panel.Width, Panel.Height);
            //Console.WriteLine("moonlightHost1: ({0},{1})", Panel.Width * current_scale, Panel.Height * current_scale);

			moonlight.SetSizeRequest((int)(Root.Width * current_scale), (int)(Root.Height * current_scale));
                            
			//Console.WriteLine("Canvas2: ({0},{1})", Panel.Width, Panel.Height);
			//Console.WriteLine("moonlightHost2: ({0},{1})", Panel.Width * current_scale, Panel.Height * current_scale);

		}
		
		public void Zoom(int zoom)
		{
			Zoom(zoom, new Point(0.0, 0.0));
			
			if (ZoomChanged != null)
				ZoomChanged(this, new EventArgs());
		}
		
		public double ZoomCorrection(double value)
		{
			return value / current_scale;
		}
		
		public Point ZoomCorrection(Point point)
		{
			return new Point(ZoomCorrection(point.X), ZoomCorrection(point.Y));
		}
		/*
		public void SaveToFile(string file)
		{
			CurrentTool.Deactivate();
			TextWriter writer = new StreamWriter(file);
			writer.Write(SerializeCanvas());
			writer.Close();
			CurrentTool.Activate();
		}
		
		public void LoadFromFile(string file)
		{
			CurrentTool.Deactivate();
			Selection.Clear();
			moonlight.LoadXamlFromFile(file);
			SetupCanvas();
			CurrentTool.Activate();
		}
		*/
		public void LoadXaml(string xaml)
		{
			CurrentTool.Deactivate();
			Selection.Clear();
			InternalLoadXaml (xaml);
			Setup();
			CurrentTool.Activate();
		}
		
        internal string xClass;
		private void InternalLoadXaml (string xaml)
		{
            XmlDocument doc = new XmlDocument ();
            doc.LoadXml(xaml);
            XmlElement root = doc.DocumentElement;
            foreach(XmlAttribute at in root.Attributes) {
	            if(at.Name.ToLower() == "x:class") {
                    xClass= at.Value;
                    root.Attributes.Remove(at);
                    break;
                }
            }
            
            //moonlight.LoadXaml ("<canvas Width=\"500\" Height=\"500\" Background=\"White\">"+doc.InnerXml+ "</canvas>");
            moonlight.LoadXaml (doc.InnerXml);
		}

        
		private void Init ()
		{
			InternalLoadXaml(view.GetSource ());
			Setup();
		}
        
        public void BeginDrag (ItemToolboxNode item, Gtk.Widget source, Gdk.DragContext ctx)
        {
			DragedControlTool tool = CurrentTool as DragedControlTool;
            if (tool != null)
                tool.BeginDrag( item, source, ctx);
        }
        
        private bool drawingMode = false;
        
        internal bool DrawingMode
        {
            get { return drawingMode; }
            set { 
                drawingMode = value;
                ToolboxProvider.Instance.NotifyItemsChanged();
            }
        }
        //TODO manage color picker
        public void ChangeSelection (FrameworkElement element)
        {
            bool test = element is Canvas || element.Parent is Canvas;
            if (drawingMode)
            {
				if (!test)
                {
                	CurrentTool = new DragedControlTool (this);
                    DrawingMode = false;
                }

                //done it here to avoid exception in tool Un/Activate
                selection.MainElement = element;
            }
            else{
                //done it here to avoid exception in tool Un/Activate
                selection.MainElement = element;
                
                if (test)
                {
                	CurrentTool = new SelectionTool (this);
                    DrawingMode = true;
                }
            }
        }

		public void UpdateCombo (System.Windows.UIElement clicked_element)
		{
			view.selectionElementCombo.Selection = (FrameworkElement)clicked_element;
		}
	}
}
